MUI-Applikationen in C

von
Carsten Jahn

Sozusagen im Anschluß an den Artikel über C-Strings wage ich mich an eine Einführung in die Programmierung von MUI-Applikationen. Weil das Thema enorm umfangreich geworden ist, ist mein Ziel nicht unbedingt, dem Leser MUI komplett darzustellen.

Ich will aber erreichen, daß der Einstieg durch diesen Artikel (und vielleicht durch weitere Artikel über MUI) erleichtert wird und daß man den Mut fassen kann, sich die Antworten auf weitere Fragen selbst zu erschließen.

Vorausgesetzt werden Grundkenntnisse über C, wo es damit brenzlig wird erkläre ich natürlich noch mehr.

Nötig ist sicher auch das Computer-Englisch, das zum Programmieren sowieso fast unverzichtbar ist. Ich kann und will MUI nicht vollständig erklären, das eigentständige Erschließen der Details ist ohne Englischkenntnisse wohl nicht möglich.

Erste Hürde: der Compiler

Bevor die Beschäftigung mit MUI überhaupt Sinn macht, sollte man in der Lage sein, einen Beispielquelltext aus dem Demo-Verzeichnis von MUI unverändert kompilieren zu können.

Das hört sich harmlos an, aber ich habe in meiner Anfangsphase mit MaxonC++ eine Weile getüftelt, bis sich der erste Source übersetzen ließ. Je nach Compiler hat man also mehr oder weniger Arbeit, mit dem Standard "SAS" wohl gar keine. Auf jeden Fall muß man natürlich die Includes und evtl. auch die Linkerlibraries (#?.lib) aus dem MUI-Developer-Archiv installieren.

Im Usenet gab es mal eine Anfrage (von Jürgen Sachs) bezüglich MUI und MaxonC++. Ich habe mich mit Jürgen weiter unterhalten.


Jürgen:
Bei mir scheitert es immer an den Includefiles so wie es ausssieht. Ich kann naemlich nicht mal ein einfaches Beispielprog compilieren :((

Ich:
Das kannst Du nie.
Ist leider so. Mich nervt es auch gewaltig, aber die Möglichkeit, einen Quelltext downzuloaden und mit Deinem ANSI-C-kompatiblen MaxonC++ zu kompilieren, existiert nicht. Kann sein, daß es umgekehrt genauso ist. Mach Dir einfach keine Hoffnungen, dann lebt sich das leichter...

Jürgen:
Waere dir trotzdem dankbar, wenn du mir die passenden Includefiles fuer MUI mit dem Maxon C++ schicken koenntest !

Ich:
Ich habe (eher zufällig) gestern Abend bei einem Freund genau das gleiche Problem gelöst (hat sicher 20-30min gebraucht), er hatte das Mui3.8-developer-Archiv und MaxonC++ Dev 4, und ich sollte was draus machen...

Also, die Includefiles kopierst Du natürlich soweit sinnvoll. Bei den pragma/ bzw. pragmas/ mußt Du die Datei nehmen, wo mehr mit "#pragma amicall" steht. Mit "#pragma libcall" kann der Compiler herzlich wenig anfangen. [...]

Die "#pragma tagcall" mußt Du einfach auskommentieren. Maxon meckert bloß.

Jetzt sollte sich der Quelltext problemlos compilieren lassen.

Mal davon abgesehen, daß Maxon unbedingt seine "casts" haben muß, z.B. reicht nicht

MUI_DisposeObject( app ); es mußMUI_DisposeObject( (Object *) app );

sein. Kann sein, daß man das umgehen kann, indem man einfach den C++-Modus des Compilers ausmacht. (Sollte man auch machen, wenn man mal was mit Hooks und Zeigern auf Funktionen macht, meine Kenntnisse reichen jedenfalls nicht soweit, den kryptischen Cast für Zeiger auf Funktionen hinzukriegen...) Das läßt sich beim Maxon über "#pragma -" und "#pragma +" steuern.

Bloß beim Linken gibt's jetzt noch Probleme. Die Tagcall-Library- Funktionen, die Du oben auskommentieren mußtest, werden natürlich in den Beispielprogrammen kräftig verwendet (und, ich glaube, auch an 500 Stellen in libraries/mui.h).

Weil Du und ich das absolut extreme Glück haben, daß MUI ein vielbenutztes Softwarepaket ist, und man sich deshalb sogar um den Maxon-Compiler gekümmert hat (und andere, die mit Tag-calls nicht gleich klarkommen), gibt es für diese Funktionen eine Linker-Library (die aus MUI_NewObject, das nicht mehr geht, ein MUI_NewObject macht, das einfach die Stackparameter in Register tut und das auch in Maxon funktionierende MUI_NewObjectA aufruft).

Also: Projekt für den Beispiel-Source anlegen, und in dem Developer-Archiv nach Dateien wie "muilr.lib" (es gibt in etwa vier davon, ich benutze "muil.lib" und "muilr.lib") suchen. ("mui.lib" aus manx/ tut es nicht.)

Mußte Dir eins ausprobieren, ist aber egal, soweit ich da durchblicke. Dieses File ist irgendwo im Developer-Archiv versteckt (zwischen den Sourcecodes?), wir haben gestern jedenfalls am längsten danach gesucht...

Wie Du siehst, MUI zu benutzen ist nicht einfach. Es liegt nicht direkt an den Headerfiles (die haben luxuriöserweise sogar "#ifdef __MAXON__"- Abfragen), sondern an dem ganzen Aufbau insgesamt.


Diese Mail hat ihm anscheinend weitergeholfen, oder er hat total aufgegeben, mich erreichte jedenfalls keine weitere Antwort.

Wer Probleme bei der Compilierung eines Beispiels hat, möchte sich doch bitte beim AmZeiger oder bei mir melden, dann diskutieren wir öffentlich weiter.

Wenn es mit anderen Compilern nicht klappt, sind sicher auch die MUI-Mailingliste: email mit dem Betreff "SUBSCRIBE" an mui-request@sunsite.informatik.rwth-aachen.de und die Amiga-Programmierbretter im Usenet ein Tip.
(Z-Netz/Rechner/Amiga/Programmieren und Comp/Sys/Amiga/Programmer)

Basics von MUI:

MUI funktioniert "objektorientiert", trotzdem braucht man keinen C++-Compiler, und man versteht es (hoffentlich) auch ohne OOP-Kenntnisse.

- Objekte, Attribute

MUI stellt eine Reihe von Klassen bereit, z.B. für Listen und Stringgadgets, zur Laufzeit des Programms werden gewissermaßen "Instanzen" von diesen Klassen gebildet, also Objekte im MUI-Sprachgebrauch. Nochmal auf deutsch: das Programm holt sich von MUI z.B. drei Stringgadget-Objekte, damit sich eine Adresse eingeben läßt (Name, Straße, Ort).

Jede Objektklasse bietet eine Anzahl von Attributen und Methoden. Wenn wir beim Beispiel von Strings bleiben, finden wir jedoch nur Attribute vor (String hat keine Methoden).

Ein Blick in Autodocs/MUI_String.doc bestätigt das, die Attribute von String heißen z.B. "MUIA_String_Accept" und "MUIA_String_Contents".

Was sind diese Attribute, wie benutzt man sie?

Wie in Taglisten beim AmigaOS kann man für jedes Attribut einen Wert übergeben. Der String verwaltet unter dem Attribut MUIA_String_Contents den Inhalt des Strings.

Man kann das Attribut setzen (set), auslesen (get) und initialisieren, also: man kann dem String zur Laufzeit des Programms einen neuen Inhalt geben, man kann jederzeit erfragen, welchen Inhalt er gerade hat (weil der Inhalt schließlich durch den Anwender des Programms geändert werden kann), und man darf bereits bei der Erstellung des String-Objekts, also wenn man es sich gerade von MUI holt, vorbestimmen, was der erste Inhalt sein soll (Initialize).

In den Autodocs steht bei beiden Attributen der Text "[ISG]" hinter den Attributnamen. Das bedeutet genau die oben beschriebenen Möglichkeiten. Ein Attribut mit "[..G]" läßt sich nur auslesen.

Attribute beschreiben also den Zustand eines Objekts. Mit Makroaufrufen wie

set( Mystring, MUIA_String_Contents, "Hallo!" );

[definiert in libraries/mui.h als SetAttrs(), jedoch sind dort die Parameter vertauscht] lassen sich Attribute setzen.

Einen Stringinhalt fragt man so wieder ab:

char *s; get( Mystring, MUIA_String_Contents, &s ); printf("Im String steht %s\n.", s );

Die Referenz auf s (&s) ist nötig, weil String keinen String in s hereinkopiert, sondern nur einen Zeiger auf den String in s zurückgibt.

Wie man an Mystring herankommt, zeige ich später. Es genügt zu wissen, daß Mystring ein Zeiger auf das bestimmte String-Objekt ist.

Ebenso kann man, wenn die Situation es erfordert, mit MUIA_String_Accept verfahren. Dann nimmt das Stringgadget nur noch bestimmte Zeichen an. Läßt man ein Attribut unangetastet, behält es seinen Standardwert, im Beispiel kann man alle üblichen Zeichen ins Stringgadget eintragen.

Methoden

Seltener als Attribute sind Methoden zu finden. Methoden lassen das Objekt auf eine bestimmte Weise aktiv werden. Proportional-Gadgets (also Proportional-Objekte) bieten die Methode MUIM_Prop_Increase.

Wie die Autodoc erklärt, kann man damit das Proportionalgadget um einen bestimmen Betrag weiterscrollen lassen. MUIM_Prop_Increase erleichtert so den Vorgang, erst die Position des Prop-Sliders mit MUIA_Prop_First auszulesen, um einen Betrag zu addieren und MUIA_Prop_First wieder zu setzen.

Eine Methode ist wie eine Funktion in einem Programm, sie kann Parameter erwarten. Hier ist der einzige Parameter der Betrag, um den sich der Prop vorwärtsschiebt. Ein möglicher Aufruf der Methode:

DoMethod( Myprop, MUIM_Prop_Increase, 1 );

DoMethod ist, wie Set/GetAttr eine BOOPSI-(Intuition-)Systemfunktion. Doch das muß nicht weiter interessieren.

Ein transparenteres Beispiel bietet z.B. die List-Klasse: mit dem Aufruf von MUIM_List_Sort werden die Einträge einer Liste von sortiert. Ein Beispielaufruf findet sich in den Autodocs (MUI_List.doc).

Vererbung im Klassenbaum

Viele Objekte verfügen über die gleichen Eigenschaften.

Man kann ein Cycle wie jedes andere sichtbare Bedienelement "disablen", also unbedienbar machen. Ein grauer Schleier steht über dem Gadget. Es wäre denkbar, das jedes Gadget ein passendes Attribut besitzt, mit dem man den Disabled-Zustand ändern kann, also MUIA_Cycle_Disabled, MUIA_String_Disabled,... all diese Attribute existieren nicht.

Dafür gibt es nur MUIA_Disabled, das für Slider, Cycles und andere gleichermaßen gesetzt und ausgelesen (und initialisiert...) werden kann.

Das ist nicht nur eine praktische Abkürzung. Wenn man die Autodocs von MUI durchsucht, in der Shell z.B. mit

13.Work:.../Autodocs> search #? MUIA_Disabled QUICK

erhält man die Ausgabe

Work:util/libs/MUI/Developer/Autodocs/MUI_Area.doc
    10 Area.mui/MUIA_Disabled
   253 .Area.mui/MUIA_Disabled
   256  MUIA_Disabled -- (V4 ) [ISG], BOOL
   274     string, 3, MUIM_Set, MUIA_Disabled, TRUE);
   277     string, 3, MUIM_Set, MUIA_Disabled, TRUE);
   280     string, 3, MUIM_Set, MUIA_Disabled, FALSE);
Work:util/libs/MUI/Developer/Autodocs/MUI_Notify.doc
   489  DoMethod(xxx, MUIM_MultiSet, MUIA_Disabled, TRUE,
   585               "negative" attributes like MUIA_Disabled.

In der Zitatzeile 256 sieht man den Definitionskopf von MUIA_Disabled.

MUIA_Disabled ist also ein Attribut der Klasse "Area". Schaut man an den Anfang des Includes libraries/mui.h, sieht man, daß ziemlich viele Klassen von Area abhängen, also von Area "abgeleitet" sind.

Das Besondere dabei: Alle Klassen, die in diesem Objektbaum an "Area" hängen, erben die Eigenschaften von Area.

Wie dort auch steht, ist Area "base class for all GUI elements", also die Basisklasse für alle im Fenster sichtbaren Objekte.

Das hat die weitreichenden Folgen, daß sich nicht nur alle darstellbaren Objekte (welche das sind, sieht man im Baum) MUIA_Disable'n lassen, alle können auch mit einer bestimmten Taste aktiviert werden (MUIA_ControlChar) oder bieten Drag&Drop-Unterstützung.

- Verkettung der Objekte als MUI-Applikation

Jede Applikation besitzt ein Application-Objekt, das Aussagen über den Autor des Programms aufnimmt, den Namen des ARexx-Ports definiert und ähnliche Festlegungen vornimmt.

Das Application-Objekt hält auch eine Liste auf Zeiger für die Fenster, die die Applikation bereithält (ob offen oder nicht). Auch ein für alle Fenster geltendes Menü ist setzbar.

Die Fenster können ihrerseits ein Menü haben, vor allem haben sie einen Fensterinhalt: eine (vertikale) Gruppe.

Ähnlich den Verzeichnissen auf einer Festplatte, kann die Gruppe (fast) beliebige Objekte beinhalten, vor allem Gadgets und weitere Gruppen. Gruppen können bei ihrer Erstellung zu horizontalen, vertikalen und Column- (Spalten-) Gruppen erklärt werden.

Dieses Attribut wirkt sich auf die graphische Anordnung der Kinder der Gruppe aus. Für den ReqTools-Filerequester, den ich gerade vor mir habe, hoffentlich jedem im Bild, benötige ich eine vertikale Gruppe, die ihrerseits

enthält.

(Zur Vollständigkeit: ColumnGroups arrangieren die Child-Objekte wie in einer Tabelle, man übergibt die Anzahl der Spalten dieser Tabelle, daher der eigenwillige Name.)

MUI erledigt die Errechnung der endgültigen X/Y-Positionen der Gadgets im Fenster automatisch. Diese Automatik ist im Prinzip auch schon das wichtigste Argument für einen Programmierer, MUI überhaupt zu benutzen.

Natürlich ist der "Nachteil", daß man immer ein Objekt braucht, und nicht nach Lust und Laune im Fenster herumzeichnen kann. Für Gadgets und Anzeigen, die vom (weitläufig gefaßten) Standard abweichen, den MUI bietet, gibt es die Möglichkeit, eine eigene Klasse (Custom Class) zu schreiben, die sich aus einem anderen MUI-Objekt ableitet. (Zum Beispiel ein Slider mit Minuten:Sekunden-Anzeige).

Davon mal abgesehen, erlaubt es MUI durch die Custom Classes, ganz elegant einen Editor zu schreiben, der mehrere Fenster gleichzeitig geöffnet haben kann. (Man tut das Editor-Programm sozusagen in eine Custom Class, die von der MUI-Windowklasse abgeleitet ist, und muß sich dann nur mehrere Objekte seiner eigenen Klasse besorgen.)

Ob solche Anwendungen durch MUI einfacher zu programmieren sind, muß jeder selbst entscheiden.

Notifies

In der realen Anwendung muß das eigentliche Programm natürlich auf die Eingaben des Benutzers reagieren können. Das wird über sogenannte Notifies erreicht.

Ein Notify verbindet die Veränderung eines Attributes (z.B. MUIA_Pressed bei einem normalen Druckknopf-Button) mit der Ausführung einer Methode. In älteren MUI-Programmen gab es eine große Hauptschleife, in der mit ULONG result = DoMethod( (Object *) app,MUIM_Application_Input,&signal); alle Eingaben an die Applikation geholt wurden, die Notifies aller Gadgets riefen die Methode MUIM_Application_ReturnID des Applikations-Objekts mit einer speziellen ID als Parameter auf (siehe oben). Diese ID wurde als `result` bei obigem Aufruf zurückgegeben, so war bestimmt, welches Gadget sich über den Notify gemeldet hatte.

Um beim Button zu bleiben: normalerweise tritt die Aktion erst ein, wenn man die Maustaste losläßt, während der Pfeil noch über dem Button schwebt. MUI-mäßig heißt das, daß man wartet. bis das Attribut MUIA_Pressed des Buttons wieder FALSE wird.

Wenn das geschieht, soll die ID "5" an das Applikationsobjekt geschickt werden, damit das Programm auf die Eingabe reagieren kann. Üblicherweise verwendet man keine Zahl an sich, sondern eine Konstante.

#define OKAY_GADGET 5 Die benutzen wir jetzt beim Aufbau des Notifies:DoMethod( okay_gad,MUIM_Notify,MUIA_Pressed,FALSE,app,2, MUIM_Application_ReturnID, OKAY_GADGET );

Zu dem Aufruf dieser Methode gibt's viel zu sagen... erstmal unterstützt unser Button [unser okay_gad ist ein (Object *), es referiert den Button] die Methode MUIM_Notify, weil er die Methoden der der Notify-Klasse durch den MUI-Hierarchiebaum geerbt hat.

MUIM_Notify übernimmt die Parameter, also alles, was folgt. Erstmal geht es um das zu überwachende Attribut von okay_gad, nämlich MUIA_Pressed, und um den Wert, den dieses Attribut annehmen soll. In diesem Fall kann man konkret "FALSE" sagen, bei einem Slider will man sicher immer benachrichtigt werden, nicht nur, wenn gerade der Betrag "42" eingestellt ist.

In diesem Fall übergibt man MUIV_EveryTime als "Triggervalue" anstatt dem konkreten FALSE.

Ist die Notify-Ursache definiert, folgt eine Wirkung. Der Notify ist generell in der Lage, die Methode eines beliebigen Objekts mit bestimmten Parametern aufzurufen. Hier richtet sich der Notify an "app", das ist der Zeiger auf das Application-Objekt.

Nun folgt die Anzahl der folgenden Parameter, kann sicher ins Auge gehen, wenn man mit verschiedenen Notifies experimentiert und dabei vergißt, diesen Wert auf dem laufenden zu halten.

Direkt nach diesem Parameter erwartet Notify eine Methode der eben genannten Klasse, die bedient werden soll, in unserem Fall "MUIM_Application_ReturnID", eine Methode von Application. Wer's nicht glaubt, darf natürlich in den Autodocs nachsehen. :o)

Diese Methode erwartet ihrerseits nur einen Parameter, nämlich die ReturnID, die an die Applikation übergeben werden soll. Notify nimmt bei Auftreten des Ereignisses die Methode und Paramter und macht damit ein eigenes DoMethod mit dem angegebenen Objekt (hier app).

Okay, so soll es nicht gemacht werden, hat Stefan Stuntz entschieden, und damit hat er sicher Recht, denn große Programme bekommen auf diese Weise eine ellenlange switch-Abfrage für die zurückgegebenen Return-IDs.

Stattdessen wird befürwortet, bei einem Programm mit mehreren Fenstern die Abfrage der Gadgets für alle Fenster nicht in einer Hauptschleife mit case zu machen, sondern für jedes Fenster eine eigene Custom Class von der Klasse Window zu machen (auch, wenn man sich sicher ist, dieses Fenster nie mehr als einmal gleichzeitig auf den Schirm bringen zu wollen).

Natürlich haben Custom Classes, wie die anderen MUI-Klassen, selbstdefinierte Methoden. Der Notify ruft dann eine Methode der eigenen Klasse auf, diese Funktion ist dann ausschließlich für den Fall da, daß jemand auf den Button drückt, und handhabt das entsprechend.

Eine weitere Programmiertechnik, die eine große Hauptschleife überflüssig macht, benutzt MUIM_CallHook (siehe Notify.mui). Diese Methode bewirkt den Aufruf einer Hook-Funktion, wenn ein bestimmtes Ereignis eintritt.

Eine Hook-Funktion ist eine normale C-Funktion, die spezielle Anforderungen erfüllt (man schreibt "SAVEDS ASM" davor, damit der Compiler hier aufpaßt, Maxon generiert sowieso Hook-fähigen Code, deshalb sind die "SAVEDS ASM"-Direktiven nicht nötig) und mit Hilfe der Hook-Struktur (die einen Zeiger auf die Funktion erhält) aufgerufen werden kann. Ein Beispiel, wie sowas aussehen kann, gibt u.a. in SaveLoad.c,

Randbemerkung:

Kennt jemand (noch) das Prinzip F-G-O? Funktionalität, Geschwindigkeit, Oberfläche, in dieser Reihenfolge sollte der Programmierer sein Werk voranbringen.

Die Prioritäten haben sich da wohl verändert, und wer so weit gekommen ist, beim Druck auf einen Button ein "Hallo!" per printf() aus einer Methode der eigenen Custom Class zu bekommen, freut er sich bestimmt (zu Recht) wie ein Schneekönig und wird vor lauter Erschöpfung und aufgestauter Frustration (sicher ist das Programm vorher zwanzigmal abgestürzt) den Rechner ausschalten. Über die Geschwindigkeit von MUI läßt sich streiten, und von der Funktionalität an sich steht noch nicht eine Zeile.

Ein Beispiel:

(wird sicher Zeit)

Man nehme Examples/Pages.c. Erstens, weil es sich ganz simpel fast nur mit dem Aufbau eines MUI-Fensters beschäftigt, zweitens, weil ich dann kein eigenes erfinden muß und drittens, weil das fünfte Wort gleich "Sex" ist :-). Vielleicht wecken wir damit die bereits eingeschlafenen Leser wieder auf. :o)) Und rein ins Gewühle:

int main(int argc,char *argv[]) { APTR app,window; init();

Was will uns Stefan damit sagen? Init?

Ein blick in demo.h, dem einzigen Include dieses Files, wirkt Wunder. Man hat nur Platz gespart, der dicke Berg an Includes steht in demo.h, und ziemlich weit hinter fail(), als vorletzte Funktion, findet sich init().

Die macht netterweise etwas ganz Bekanntes, sie macht die Library auf. Schließlich ist MUI immer noch "eine" Shared Library...

Und schon geht's glashart los, kaum etwas erinnert hier noch an C-Quelltext:

app = ApplicationObject, MUIA_Application_Title , "Pages-Demo", MUIA_Application_Version , "$VER: Pages-Demo 17.6 (18.08.96)", MUIA_Application_Copyright , "©1992/93, Stefan Stuntz", MUIA_Application_Author , "Stefan Stuntz", MUIA_Application_Description, "Show MUIs Page Groups", MUIA_Application_Base , "PAGESDEMO",

Naja, ein wenig Infos... Infos zu diesen Dingen in der Application-Autodoc.

SubWindow, window = WindowObject,

Spitze, die Applikation hat auch ein Fenster. Man erinnere sich an das, was ich weiter oben über den baumartigen Aufbau der Objekte gesagt habe.

MUIA_Window_Title, "Character Definition", MUIA_Window_ID , MAKE_ID('P','A','G','E'),

ID, nur damit die Fenster intern unterschieden werden können, wenn MUI einen "Snapshot" der Fenstergrößen und -Positionen macht.

WindowContents, VGroup,

Es folgt der Inhalt des Fensters, eine vertikale Gruppe,

Child, ColGroup(2),

die als Kind gleich eine ColumnGroup hat, sie erzeugt eine unsichtbare Tabelle mit (hier) zwei Spalten, in die alle folgenden Kinder (Childs) von links nach rechts, von oben nach unten eingefügt werden.

Wichtig: die Anzahl der Objekte einer Spaltengruppe muß durch die Anzahl der Spalten teilbar sein, Reste mag MUI nicht. Für Leerplätze bietet MUI das HVSpace-Objekt, immer anwendbar. Es ist ein beliebig ausdehnbares, unsichtbares Objekt.

Child, Label2("Name:"), Child, String("Frodo",32), Child, Label1("Sex:" ), Child, Cycle(Sex),

Labels sind die Beschriftungen, warum Label2 und 1, verrät ein Blick in libraries/mui.h (es geht dabei um die spezielle Ausrichtung, ein String ist ja eigentlich doppelt umrahmt).

End,

Die Gruppe ist zu Ende! Alle weiteren Childs beziehen sich auf die nächsthöhere Gruppe, die VGroup.

Bevor ich weiter in das Programm einsteige, schulde ich noch eine Erklärung, was diese bizarren Definitionen in einem C-Quelltext sollen.

Schlauerweise sind die ganzen Schlüsselwörter C-Makros, aus End wird zum Beispiel "TAG_DONE)". Wo die Klammer aufgeht? Zum Beispiel in VGroup-Makro. Ein Compilerfehler wegen unbalancierter Klammern deutet also meistens darauf hin, daß man ein "End" vergessen hat.

Schmerzhaft ist auch das Vergessen von "Child" vor den Gruppenkindern. Mit dem Unterschied, daß der Compiler hier keinen Fehler meldet...

Weiter im Programm: Child, VSpace(2),

Das ist eine Leerstelle, die vertikal zwei Pixel hoch ist, aber beliebig breit werden kann (eher eine Leerzeile), sie dient der optischen Optimierung. :o)

Child, RegisterGroup(Pages), MUIA_Register_Frame, TRUE,

Damit macht man so eine nette Registergruppe mit Reiterchen. In "Pages" finden sich die Titel der Reiter, wie auch die Stringeinträge als Zeiger auf ein Feld von Zeigern auf Strings, mit einem Null-Zeiger abgeschlossen (siehe Anfang des Quelltextes).

Initialisiert man MUIA_Register_Frame übrigens mit FALSE, wird der graphische Registerkram nicht sichtbar. Der Benutzer kann die Seiten nicht umschalten, wohl aber das Programm. Damit lassen sich einfach Effekte erzielen, bei denen einige Gadgets verschwinden und neue an ähnlicher Stelle auftauchen.

Child, HCenter(Radio(NULL,Races)), Hoppla, jedes weitere Kind in dieser Ebene kommt auf eine eigene Seite! Hier das Radio-Gadget mit den Rassen durch HCenter horitontal (unter vertikal sowieso) zentriert. Child, HCenter(Radio(NULL,Classes)), dito. Jetzt für Klassen. Meine Güte, Stefan geht wieder voll ran. Child, HGroup, Child, HSpace(0), Child, ColGroup(2), Child, Label1("Cloak:" ), Child, CheckMark(TRUE), Child, Label1("Shield:"), Child, CheckMark(TRUE), Child, Label1("Gloves:"), Child, CheckMark(TRUE), Child, Label1("Helmet:"), Child, CheckMark(TRUE), End, Child, HSpace(0), End, So hat man sich das immer vorgestellt, oder? Im Prinzip ist das schon nichts Neues mehr, was CheckMark macht, dürfte klar sein (TRUE ist der Default, also abgehakt.) Child, ColGroup(2), Child, Label("Experience:" ), Child, Slider(0,100, 3), Child, Label("Strength:" ), Child, Slider(0,100,42), Child, Label("Dexterity:" ), Child, Slider(0,100,24), Child, Label("Condition:" ), Child, Slider(0,100,39), Child, Label("Intelligence:"), Child, Slider(0,100,74), End, Mann, intelligent sind die auch noch... End, End, End, End; Der klassische Abgang. Man beachte das Semikolon.

Oben habe ich geschrieben, ein End macht jeweils eine Klammer zu. Konsequenz: alles eben gesehene ist ein einziger großer Funktionsaufruf (von MUI_NewObject()), der das Resultat weiterer Funktionsaufrufe als Parameter enthält. Also kein Wunder, warum man für MUI-Programme 10-20kB Stack bereithalten sollte.

Dabei sind auch Fehlersituationen elegant gelöst. Wenn MUI zwar noch die RegisterGroup holen kann, aber für den vorletzten CheckMark darin nun wirklich keinen Speicherplatz mehr finden kann, liefert CheckMark() NULL zurück.

Die ColGroup, in der das Checkmark steht, sieht sich veranlaßt, alle bereits bekommenen Kinder zu MUI_DisposeObject()'n, also freizugeben, und liefert ihrerseits NULL... bis schließlich app NULL ist. Und das sollte man schon testen.

if (!app) fail(app,"Failed to create Application.");

Wie beruhigend, daß das auch im Beispiel passiert...

DoMethod(window,MUIM_Notify,MUIA_Window_CloseRequest,TRUE, app,2,MUIM_Application_ReturnID,MUIV_Application_ReturnID_Quit);

Toll, ein Notify. Schließlich soll sich das Programm beenden, wenn das Fenster geschlossen wird. MUIV_Application_ReturnID_Quit ist ein spezieller Wert (wie alle MUIV_#?), den wir unten genauso speziell abfragen werden. MUI liefert ihn auch, wenn man (bei einem Commodity) mittels Exchange ein Remove macht.

set(window,MUIA_Window_Open,TRUE);

Ein Attribut wird gesetzt. Okay, es wäre natürlich möglich, eine Methode MUIM_Window_Open zu definieren, die man mit DoMethod aufruft, aber es ist eben so gelöst.

Das Fenster wird übrigens erst jetzt geöffnet.

{ Ein eigener Scope wegen ULONG sigs = 0; der Variablen? Naja, wer's mag. while (DoMethod(app,MUIM_Application_NewInput,&sigs) != MUIV_Application_ReturnID_Quit) {

Und jetzt gleich so eine Hammerzeile. Wir holen uns damit die ReturnIDs.

Eigentlich erwarten wir nur eine, und die wird der Übersichtlichkeit halber gleich mit abgefragt. So spart man die result- und quit-Variable, die eine bräuchte man zur Abfrage mehrerer IDs, die andere als Abbruchbedingung.

if (sigs) { sigs = Wait(sigs | SIGBREAKF_CTRL_C); if (sigs & SIGBREAKF_CTRL_C) break; }

Einfach abschreiben. :o) Im Ernst, MUI soll ja nicht dauernd abgefragt werden, sondern nur, wenn was passiert, deshalb Wait.

Weil das Beispiel konsequenterweise auf die Abfrage der Gadgets verzichtet, ist die Hauptschleife eher kurz.

Ne Klasse gibt's sowieso nicht, daß Beispiel ist vom August 1996, wie uns der Header informiert. Da hab ich wohl nochmal Schwein gehabt, aber ich denke, für die nächste AmZeiger-Ausgabe erwartet mich wohl Schlimmes...

} } set(window,MUIA_Window_Open,FALSE); Jupp, zumachen... wir sind fertig. /* ** Shut down... */ fail(app,NULL); }

Shut up! fail() ruft MUI_DisposeObject( app ) auf, und gibt somit den ganzen MUI-Stuff frei, außerdem wird die Library wieder geschlossen.

DisposeObject hat die Eigenschaft, auch alle verbundenen Objekte freizugeben, deshalb wird nicht nur das Application-Objekt entfernt, sondern die gesamte Applikation (mit den bekannten Fenstern etc.).

Na, war das ein kleiner Überblick? Wenn ich mich dazu aufraffen kann, werde ich in einem weiteren Kursteil auf Wunsch noch ein anderes MUI-Beispiel und die Sache mit den Klassen "erklären".

Dazu muß ich sagen, daß ich selbst auch noch nicht lange so richtig durchblicke, für echt ausgeflippte Sachen muß man sich immer extra einarbeiten und MUI im wahrsten Sinne des Wortes erforschen (auf welchem Weg stürzt es nicht ab?). Ist vielleicht auch nur eine persönliche Erfahrung.

Stefan, kriege ich jetzt ein Keyfile auf Lebenszeit? :-))

Anyway, nachdem nun die wichtigsten Innereien auch nur kurz angeleuchtet sind, dürfte jedem klar sein, daß MUI den Sharebetrag und jedes Upgrade wert ist.

Über Meinungen und Feedback (Adresse s.oben) wäre ich hocherfreut, naja, soviel Zeit habe ich eigentlich nicht und der Text hier ist in etwa vier Stunden entstanden, aber die Mühe war es doch wert, oder?

Viel Erfolg bei der tieferen Erkundung von MUI wünscht,

Carsten


Prev Inhaltsverzeichnis Next
©`97Der AmZeiger